#pragma once

#include <queue>
#include "transformable.h"
#include "openaudio_global.h"
#include <alut.h>
#include <osg/Vec3>
#include "actorbase.h"
#include "scene.h"
#include "commonshape.h"

#define BIT(a) (long(1L<<long(a)))

namespace OpenAudio
{
	class OPENAUDIO_EXPORT Sound : public CoreBase::Transformable
	{
	public:
		typedef void (*CallBack)(Sound* sound, void* param);
		//class FrameData : public osg::Referenced
		//{
		//public:
		//	FrameData();
		//	FrameData(float gain, float pitch, unsigned int playing = 0);
		//protected:
		//	~FrameData();
		//private:
		//	FrameData(const FrameData& d);
		//	FrameData& operator =(const FrameData& d);
		//	friend class Sound;
		//	float _gain, _pitch;
		//	unsigned int mPlaying;
		//};
		enum Command
		{
			NONE = 0L,
			LOAD,
			UNLOAD,
			PLAY,
			PAUSE,
			STOP,
			REWIND,
			LOOP,
			UNLOOP,
			QUEUE,
			GAIN,
			PITCH,
			POSITION,
			DIRECTION,
			VELOCITY,
			ABS,
			REL,
			REF_DIST,
			MAX_DIST,
			ROL_FACT,
			MIN_GAIN,
			MAX_GAIN,
			kNumCommands
		};

	public:
		static const char* kCommand[kNumCommands];
		Sound(CoreBase::ActorBase* owneractor,const QString& name = "Sound");
		public slots:
			virtual void onMessage(MessageData*);
	protected:
		virtual ~Sound();
		unsigned int getState(unsigned int flag) const 
		{ 
			return _commandState & BIT(flag); 
		}
		void setState(unsigned int flag);      
		void resetState(unsigned int flag) 
		{
			_commandState &= ~BIT(flag);
		}
		bool restoreSource();
	public:
		virtual void addedToScene(CoreBase::Scene* scene);
		void setFilename(const std::string& file);
		void clear();      
		bool releaseSource();
		void runAllCommandsInQueue();
		ALint getBuffer();
		const char* getFilename() 
		{ 
			return _filename.c_str();
		}
		ALuint getSource() 
		{ 
			return _source; 
		}
		bool isInitialized() 
		{ 
			return _isInitialized;
		}
		bool isLooping() const;
		bool isListenerRelative() const;
		int isPaused() const
		{
			return getState(PAUSE);
		}
		int isPlaying() const 
		{ 
			return getState(PLAY);
		}
		int isStopped() const
		{ 
			return 
				getState(STOP);
		}
		void setBuffer(ALint b);
		void setInitialized(bool isInit) 
		{
			_isInitialized = isInit;
		}
		virtual void setPlayCallback(CallBack cb, void* param);
		virtual void setStopCallback(CallBack cb, void* param);
		void setSource(ALuint s);
		void play();
		bool playImmediately();
		void pause();
		void pauseImmediately();
		void stop();
		void stopImmediately();
		void rewind();
		void rewindImmediately();
		void setLooping(bool loop = true);
		void setGain(float gain);
		float getGain() const;
		void setPlayTimeOffset(float seconds);
		float getPlayTimeOffset() const;
		void setPitch(float pitch);
		float getPitch() const;
		void setListenerRelative(bool relative);
		void setTransform(const CoreBase::Transform& xform,
			CoreBase::Transformable::CoordSysEnum cs = CoreBase::Transformable::ABS_CS);
		void setPosition(const osg::Vec3& position);
		void getPosition(osg::Vec3& position) const;
		osg::Vec3& getPosition();
		void setDirection(const osg::Vec3& direction);
		void getDirection(osg::Vec3& direction) const;
		osg::Vec3 getDirection();
		void setVelocity(const osg::Vec3& velocity);
		void getVelocity(osg::Vec3& velocity) const;
		osg::Vec3 getVelocity();
		void setMaxDistance(float dist);
		float getMaxDistance() const;
		void setReferenceDistance(float dist); 
		float getReferenceDistance() const;
		void setRolloffFactor(float rolloff);
		float getRolloffFactor() const;
		void setMinGain(float gain);
		float getMinGain() const;
		void setMaxGain(float gain);
		float getMaxGain() const;
		//FrameData* createFrameData() const;
		//void useFrameData(const FrameData* data);
		//FrameData deserialize(const FrameData* data);
		//XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* Serialize(const FrameData* d, XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* doc) const;
		//float getDurationOfPlay() const;
		void setSoundMode(unsigned int mode);
	protected:
		std::string									_filename;
		CallBack									_playCB;
		void*										_playCBData;
		CallBack									_stopCB;
		void*										_stopCBData;
		std::queue<Command>							_command;
		unsigned int								_commandState;
		ALuint										_source;
		ALint										_buffer;
		bool										_isInitialized;
		ALfloat										_gain;
		ALfloat										_pitch;
		ALfloat										_secondOffset;
		ALfloat										_maxDistance;
		ALfloat										_referenceDistance;
		ALfloat										_rolloffFactor;
		ALfloat										_minGain;
		ALfloat										_maxGain;
		bool										_listenerRelative;
		osg::Vec3									_position;
		osg::Vec3									_direction;
		osg::Vec3									_velocity;      
		bool										_userDefinedSource;
		CoreBase::Scene*							_sender;
		osg::ref_ptr<CoreBase::CircleShape>			_circleShape;
		unsigned int								_mode;
	};
}